package org.nightknight.satobee.core;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

public abstract class Conversation {

    public static final String connectFailed = "Connect Failed!!!";
    private List<IUsrOutListener> usrOutListeners = new ArrayList<IUsrOutListener>();
    private List<IMesRecListener> listeners = new ArrayList<IMesRecListener>();
    private Socket s = null;
    private Contact contact;
    private int port = 8199;
    private DataOutputStream out;
    private DataInputStream in;
    //监听端口接收到的信息，并触发事件，当socket被关闭时终止线程
    private Thread listener = new MessageListener();
    private Conversation outer = this;

    /**
     * 初始化一个Socket，之后的使用之前需要先连接上服务器，调用connect方法。
     */
    public Conversation() {
        this.addMessageReceivedListener(new IMesRecListener() {

            public void messageReceived(Message message) {
                if (message.getType() == 2) {
                    if (message.getContent().charAt(0) == 's') {
                        fileMesReceived(message);
                    } else if (message.getContent().charAt(0) == 'a') {
                        fileAccepted(message);
                    } else if (message.getContent().charAt(0) == 'r') {
                        fileRefused(message);
                    }
                }
            }
        });
    }

    /**
     * 构造函数，同时连接上服务器
     * @param contact 会话的另一方
     */
    public Conversation(Contact contact) {
        this();
        
        this.contact = contact;
        this.connect(contact);
    }

    /**
     * 直接将一个构造好的Socket传进来，用于封装服务器端接受的Socket
     * @param s
     */
    public Conversation(Socket s) {
        this();
        
        this.s = s;
        contact = new Contact(s.getInetAddress());

        listener.start();
    }

    /**
     * 关闭连接
     */
    public void closeConnection() {
        try {
            if (s != null) {
                s.close();
            }

            if (out != null) {
                out.close();
            }

            if (in != null) {
                in.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Logger.getLogger(Conversation.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    /**
     * 连接到指定的服务器
     * @param url
     * @param port
     */
    public final boolean connect(Contact contact) {
        try {
            if (null == s) {
                s = new Socket(contact.getAddresss(), port);
            } else if (s.isClosed() || (!s.isConnected())) {
                s.connect(new InetSocketAddress(contact.getAddresss(), port));
            }

            listener.start();
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            Logger.getLogger(Conversation.class.getName()).log(Level.SEVERE, null, e);
            return false;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            Logger.getLogger(Conversation.class.getName()).log(Level.SEVERE, null, e);
            return false;
        }

        return true;
    }

    // 根据type和message封装Message(用于发送)
    public Message makeMessage(int type, String message) {
        Message mes = new Message();
        
        mes.setReceiver(getContact());
        mes.setSender(Server.getMe());
        mes.setSendTime(new Date());
        mes.setType(type);
        mes.setContent(message);

        return mes;
    }
    
    // 根据type和message风转Message（用于接受）
    public Message makeMessage(int type, String message, long time) {
        Message mes = new Message();
        
        mes.setType(type);
        mes.setContent(message);
        mes.setSendTime(new Date(time));
        mes.setReceiver(Server.getMe());
        mes.setSender(contact);

        return mes;
    }

    /**
     * 向服务器端发送信息
     * @param message 所要发送的信息
     * @return boolean 发送是否成功
     */
    public boolean sendMessage(Message mes) {
        boolean succ = true;
        try {

            //应当把整个消息的数据发送过去，而不是仅仅只发送内容……
            //        String message = mes.getContent();
            if ((s.isClosed() || (!s.isConnected())) && !this.connect(contact)) {
                Logger.getLogger(Conversation.class.getName()).log(Level.SEVERE, connectFailed);
                this.fireIUsrOutListener(contact);
                return false;
            }
            //使用DataOutput
            out = new DataOutputStream(s.getOutputStream());
            out.writeInt(mes.getType());
            out.writeLong(mes.geteSendTime().getTime());
            byte[] bytes = mes.getContent().getBytes();
            out.writeInt(bytes.length);
            out.write(bytes);
        } catch (IOException ex) {
            Logger.getLogger(Conversation.class.getName()).log(Level.SEVERE, null, ex);
            succ = false;
        }

        return succ;
    }
    
    public void fileAccepted(Message message) {
        FileServer.getFileServer().fileAccepted(new File(message.getContent().substring(1)), outer);
    }
    
    public void fileRefused(Message message) {
        FileServer.getFileServer().fileRefused(new File(message.getContent().substring(1)), outer);
    }

    // add the file to the list, and notify the sendFileThread to send it
    public void sendFile(final File file) {
        sendMessage(makeMessage(2, "s" + file.getAbsolutePath()));
        FileServer.getFileServer().sendFile(file, this);
    }
    
    public void acceptFileRec(File rFile, String file) {
        Message reply = makeMessage(2, "a" + file);
        sendMessage(reply);
        FileServer.getFileServer().acceptFileRec(file, rFile, this);
    }
    
    public void refuseFileRec(String file) {
        Message reply = makeMessage(2, "r" + file);
        sendMessage(reply);
    }
    
    public abstract void fileMesReceived(Message message);
    
    public abstract void startFileSend();
    public abstract void fileSending(int nch);
    public abstract void fileSent();
    public abstract void startFileReceive(String str);
    public abstract void fileReceiving(int nch, String str);
    public abstract void fileReceived(String str);

    public Socket getSocket() {
        return this.s;
    }

    public Contact getContact() {
        return this.contact;
    }

    /**
     * 添加一个监听器
     * @param listener
     */
    public final void addMessageReceivedListener(IMesRecListener listener) {
        this.listeners.add(listener);
    }

    /**
     * 删除一个监听器
     * @param listener
     * @return
     */
    public final boolean removeMessageReceivedListener(IMesRecListener listener) {
        return this.listeners.remove(listener);
    }

    /**
     * 触发所有的监听器
     * @param message
     */
    public final void fireMessageReceivedListners(Message message) {
        for (IMesRecListener lis : this.listeners) {
            lis.messageReceived(message);
        }
    }

    public final void addIUsrOutListener(IUsrOutListener listener) {
        this.usrOutListeners.add(listener);
    }

    public final void removeIUsrOutListener(IUsrOutListener listener) {
        this.usrOutListeners.remove(listener);
    }

    public final void fireIUsrOutListener(Contact contact) {
        for (IUsrOutListener listener : usrOutListeners) {
            listener.userLogout(contact);
        }
    }

    //--------------------------------------------------------------------------
    // Class used to listen the socket and fire messageListeners when message come
    //--------------------------------------------------------------------------
    class MessageListener extends Thread {

        @Override
        public void run() {
            if (null != s && s.isConnected() && (!s.isClosed())) {
                try {
                    Message mes = new Message();
                    in = new DataInputStream(s.getInputStream());
                    while (s.isConnected() && (!s.isClosed())) {
                        //使用DataInput格式化输入
                        //判断是否还有数据
                        if (in.available() > 0) {
                            int type = in.readInt();
                            long time = in.readLong();
                            int length = in.readInt();
                            byte[] bytes = new byte[length];
                            in.read(bytes);
                            
                            mes = makeMessage(type, new String(bytes), time);

                            fireMessageReceivedListners(mes);
                        }
                    }

                } catch (IOException ex) {
                    Logger.getLogger(Conversation.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
}